home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacWorld 1997 September
/
Macworld (1997-09).dmg
/
Shareware World
/
Comms & Internet
/
Mail*Link SMTP⁄QM Installer
/
Disk 3
/
Sources & Scripts
/
Sources
/
single.c
next >
Wrap
C/C++ Source or Header
|
1994-05-15
|
46KB
|
1,904 lines
/*
File: single.c
Contains: xxx put contents here xxx
Written by: Tom Biddulph
Copyright: © 1992 by StarNine Technologies, Inc., all rights reserved.
Change History (most recent first):
<1.10> 10/29/93 cac Add in the extended finder info to the finder field.
<1.9> 10/20/93 cac Make single.c create things in version2 applesingle.
<1.8> 6/23/93 cac Fix a joe bug from 4 years ago. Don't assume TOPS, always seek
to the offet for writing the resoruce and don't assume we padded
all entries to that point.
<1.7> 12/16/92 bid Some systems are aligning our structure declarations on long
word boundaries. This makes the "sizeof" operator return the
wrong number of bytes when used explicitly to get the structure
of the APPLESINGLE header. Instead, use a define that is the sum
of the sizes of the elements of the structures in question.
10/20/92 bid This is the original version of
"single.c". It has been moved from
UNIX/SCCS control to MPW projector
control.
To Do:
*/
/* @(#)(C) Copyright 1993 StarNine Technologies. All rights reserved. */
static char sccs_ident[] ="@(#)Copyright StarNine Technologies 1993\t\
Version 1.9 of single.c on 93/03/09 08:53:42";
/*
** Copyright (c) 1988,1989 StarNine Technologies, Inc.
** All rights reserved.
**
** Redistribution and use in source and binary forms are permitted
** provided that the above copyright notice and this paragraph are
** duplicated in all such forms and that any documentation,
** advertising materials, and other materials related to such
** distribution and use acknowledge that the software was developed
** by StarNine Technologies, Inc.
**
** Mail*Link and StarNine are Trademarks of StarNine Technologies, Inc.
** StarNine Technologies, Inc.'s corporate name and/or its Trademarks
** CANNOT be used to endorse or promote products derived from
** this software without specific prior written permission signed by a
** corporate officer of StarNine Technologies, Inc. authorizing their use.
**
** THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
** WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
** THIS SOFTWARE IS NOT WARRANTED TO BE ERROR FREE.
*/
/*
** single
** USAGE
** single [-o outputfile] [-s] [-c Creator] [-t Type] inputfile
**
** This program will split an APPLESINGLE file into data and resource forks.
** It will also translate "PACKIT 1" format files.
**
*/
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <errno.h>
#include <netinet/in.h>
#ifndef MAXNAMLEN
#include <dirent.h>
#endif /* MAXNAMLEN */
#ifndef MAXNAMLEN
#include <limits.h>
#endif /* MAXNAMLEN */
# define FSLENGTH 16
# define PADLENGTH 256L
# define FINDERPAD 0xE0
/*
** This is what the header looks like on the disk.
*/
struct diskheader
{
long disk_magic;
long disk_version;
char empty[FSLENGTH]; /* leave blank - this is only used in version 1 */
short disk_numentries;
};
/*
** The sizeof operator does not always returned the value that
** we expect for the above structure. The APPLESINGLE header
** defines that the above items be present at the start of the
** file and that a long is a 68000 long word (4 bytes) and a
** short is a 68000 short word (2 bytes). The sizeof operator
** on some systems will return the size of the above struture,
** long word aligned, when will be 2 more bytes that the actual
** data that will be contained in the APPLESINGLE header itself.
** This problem happens on SUN SPARCSTATIONS (as well as others).
*/
#define SIZEOF_DISKHEADER (sizeof(long)+sizeof(long)+FSLENGTH+sizeof(short))
/*
** This is what the entry structure looks like on disk (it directly
** follows the diskheader).
*/
struct appleentry
{
unsigned long entry_id;
unsigned long entry_offset;
unsigned long entry_length;
};
typedef struct diskheader diskheader;
typedef struct appleentry appleentry;
/*
** This structure is put together out of the above two strctures.
** This is what is visible to the rest of the world.
*/
struct appleheader
{
diskheader header_disk;
appleentry *header_entry;
int header_fd; /* descriptor open to the resource file */
int header_datafd; /* descriptor open to the data file (if it exists) */
long header_mask; /* Keep track of which entries we are reading */
struct stat header_stat; /* This is used to keep track of the size of the data file. */
char *header_dataname; /* Name of the data file */
};
typedef struct appleheader appleheader;
/*
** Macros to reduce typing...
*/
# define header_magic header_disk.disk_magic
# define header_version header_disk.disk_version
# define header_numentries header_disk.disk_numentries
/*
** The current magic numbers
*/
# define APPLESINGLE 0x00051600
# define APPLEDOUBLE 0x00051607
# define APPLEUNKNOWN 0x00000000
/*
** The only allowed version number.
*/
# define VERSION 0x00010000
# define VERSION_2 0x00020000
/*
** Macros to test for types.
*/
# define ISSINGLE(hdr) ((hdr->header_magic) == APPLESINGLE)
# define ISDOUBLE(hdr) ((hdr->header_magic) == APPLEDOUBLE)
# define ISAPPLE(hdr) ((ISSINGLE(hdr)) || (ISDOUBLE(hdr)))
/*
** Legal entry ids.
*/
# define ENTRY_DATAFORK 1
# define ENTRY_RESOURCEFORK 2
# define ENTRY_REALNAME 3
# define ENTRY_COMMENT 4
# define ENTRY_ICONBW 5
# define ENTRY_ICONCOLOR 6
# define ENTRY_FILEINFO 7
# define ENTRY_FILES_DATE_INFO 8
# define ENTRY_FINDERINFO 9
# define ENTRY_MAX 10
# ifndef min
# define min(a,b) ((a) < (b) ? (a) : (b))
# endif
/*
** This string defines the type of the following entry in the
** "packed" file. The types are one of:
** "PMag" Uncompressed file
** "PMa4" Compressed file
** "PEnd" End of file.
*/
typedef char ENCLHEADER [4];
# define PACK_END "PEnd"
# define PACK_COMP "PMa4"
# define PACK_UNCOMP "PMag"
/*
** Header for packed enclosure files. This is the structure that
** is put at the beginning of each entry in the file.
**
** NOTE: Because the 386 does QUAD alignment, I had to remove
** the CRC short at the end. Also, this structure does not include
** the "type" header (above) which is also on each entry.
*/
typedef struct
{
char fileNameSize;
char fileName[63];
char fileType[4];
char fileCreator[4];
short flags;
short locked;
long dataSize,
resourceSize,
createData,
modDate;
} ENCL;
typedef short ENCLCRC;
#define CREATOR_LEN 4
#define TYPE_LEN 4
#define BLANK_TYPE " "
#define DEF_CREATOR "UNKN" /* Default creator type */
#define DEF_TYPE "TEXT" /* Default Type */
#define DEF_FILE "single.out" /* Default output file name */
#define REALNAME 90L /* where we want to put the realname entry */
#define FINDER 146L /* where we want to put the finder entry */
#define FINDER_SIZE 32 /* size of the finder entry */
#define CREATE_MODE 0755 /* Default mode for creat() */
/*
** main
** Call get_start, and exit appropriately.
*/
main(argc, argv)
int argc;
char *argv[];
{
/*
** call the UNIX version of this program
*/
get_start(argc,argv);
exit(0);
}
/*
** get_start
** Parse the arguments passed on the command line. If the arguments make
** sense, translate the input file.
**
** Arguments
** argc Count of arguments
** argv List of arguments
**
** Returns
** Nothing
*/
get_start(argc,argv)
int argc;
char *argv[];
{
char *infile; /* Input file name */
char *output; /* Output file name */
char creator[CREATOR_LEN]; /* Creator to use */
char type[TYPE_LEN]; /* Type to use */
int got_o = 0; /* Flag for -o */
int got_f = 0; /* Flag for -f */
int got_s = 0; /* Flag for -s */
int got_c = 0; /* Flag for -c */
int got_t = 0; /* Flag for -t */
int got_per = 0; /* Were we passed a file with a '%' at the beginning of the name? */
int c; /* current flag returned by getopt */
struct stat st; /* Used to test for the output file */
int magic; /* Return value from get_magic */
extern int opterr; /* getopt external */
extern int optind; /* getopt external */
extern char *optarg; /* getopt external */
/*
** copy the default values into the creator and type fields
*/
strncpy(creator, DEF_CREATOR, sizeof (creator));
strncpy(type, DEF_TYPE, sizeof (type));
/*
** handle options
*/
while ( (c = getopt(argc, argv, "so:c:f:t:")) != EOF)
{
switch (c)
{
/*
** -s
** Split the input file into three files,
** (1) "output" containing the data fork.
** (2) "%output" containing the resource information.
** (3) "%houtput" containing only the resource header info.
*/
case 's':
got_s++;
break;
/*
** -o output
** What to call the output file.
*/
case 'o':
got_o++;
output = optarg;
break;
/*
** -c Creator
** Set the "Creator" field. This is a 4 byte field used by
** Macintoshes. Setting this only makes sense when creating
** Macintosh files.
*/
case 'c':
got_c++;
if (strlen(optarg) < 5)
{
strncpy((char *) creator, BLANK_TYPE, sizeof (creator));
strncpy( (char *) creator, optarg, strlen(optarg));
}
else
{
fprintf(stderr,
"The creator field needs to be 4 characters or less\n\n");
usage();
}
break;
/*
** -t Type
** Set the "Type" field. This is a 4 byte field used by
** Macintoshes. Setting this only makes sense when creating
** Macintosh files.
*/
case 't':
got_t++;
if (strlen(optarg) < 5)
{
strncpy((char *) type, BLANK_TYPE, sizeof (type));
strncpy((char *) type, optarg, strlen(optarg));
}
else
{
fprintf(stderr,
"The type field needs to be 4 characters or less\n\n");
usage();
}
break;
/*
** -h, or any error.
** help -- print usage message
*/
case 'h':
case '?':
default:
usage();
}
}
/*
** Make sure we got an input and an output file if appropriate.
** If the file is applesingle then convert to appledouble.
** If the file is appledouble then convert to applesingle.
** If the file is an unknown appletype then conver to applesingle.
** If the file is packed, then unpack the files as applesingle.
*/
if ( optind >= argc )
{
fprintf(stderr,"An input file name needed\n\n", infile);
usage();
}
if ( optind != (argc - 1) )
{
fprintf(stderr,"Only one input file allowed\n\n");
usage();
}
infile = argv[optind];
/*
** Figure out what kind of file this is. Based on that
** information, translate the file.
*/
magic = get_magic(infile, &got_per);
/*
** Packed APPLESINGLE files do not need an output file, so
** we should not check the existance of unneeded files.
*/
if ( magic != 1 )
{
if ( !got_o )
{
output = DEF_FILE;
printf("Using output file %s\n",output);
}
if ( stat(output,&st) != -1 )
{
fprintf(stderr,
"Output file %s already exists\n",output);
exit(1);
}
}
switch(magic)
{
/*
** Neither Single, nor double.
*/
case 0:
if (got_s)
{
fprintf(stderr,
"%s not in Apple Single format: -s option does not apply\n\n", infile);
usage();
}
make_apple(infile,output,creator,type);
break;
/*
** Apple Single format
*/
case 1:
if (got_c)
{
fprintf(stderr,
"%s is in Apple Single format: -c option does not apply\n\n", infile);
usage();
}
if (got_t)
{
fprintf(stderr,
"%s is in Apple Single format: -t option does not apply\n\n", infile);
usage();
}
unmake_apple(infile,output,got_s,got_o);
break;
/*
** Apple Double format
*/
case 2:
if (got_c)
{
fprintf(stderr,
"%s is in Apple Double format: -c option does not apply\n\n", infile);
usage();
}
if (got_t)
{
fprintf(stderr,
"%s is in Apple Double format: -t option does not apply\n\n", infile);
usage();
}
if (got_s)
{
fprintf(stderr,
"%s not in Apple Single format: -s option does not apply\n\n", infile);
usage();
}
double_to_single(infile,output,got_per);
break;
}
}
/*
** usage
**
** This function prints the usage messages, and quits.
** NOTE: This routine will exit.
*/
usage()
{
fprintf (stderr,
"usage: single [-s] [-c creator] [-t type] -o output input\n\n");
fprintf (stderr, " -s create a seperate header and resource file\n");
fprintf (stderr, " -c change the creator field in the finder\n");
fprintf (stderr, " -t change the type field in the finder\n");
fprintf (stderr, " -o the name of the output file\n");
exit(1);
}
/*
** get_magic
** This function determines the format of a file.
**
** Arguments
** name The name of the file to check.
** got_per whether the file we got starts with a percent
** This will be returned to the callee.
**
** Returns
** 2 if the file is Apple Double
** 1 if the file is Apple Single
** 0 if the file is not an Apple format
*/
get_magic(name, got_per)
char *name;
int *got_per;
{
register int fd; /* Descriptor used to read the file. */
diskheader disk; /* How an Apple single or double format
file is stored on disk. */
int size; /* Size returned by read. */
char temp[MAXPATHLEN]; /* Used to construct the '%' file for
AppleDouble format resource forks. */
/*
** open the file and try reading the header information
*/
if((fd = open(name, O_RDONLY )) == -1)
{
fprintf(stderr, "get_magic: open on %s failed: ",name);
perror("");
close(fd);
exit (1);
}
if((size = read(fd, &disk, SIZEOF_DISKHEADER)) == -1 )
{
perror ("get_magic: read failed");
close(fd);
exit (1);
}
(void) close(fd);
if ( size < SIZEOF_DISKHEADER )
return (0);
*got_per = 0;
/*
** check that the file is applesingle
*/
if (disk.disk_magic == APPLESINGLE)
{
return(1);
}
else
{
/*
** if the magic number is apple double, then we must
** have gotten the percent file
*/
if (disk.disk_magic == APPLEDOUBLE)
{
*got_per = 1;
return(2);
}
else
{
/*
** other-wise we need to check whether the percent file
** (if it exists) is apple double. To do this we
** construct a new file name, with a '%' in front.
*/
doubleName(name,temp,0);
if ((fd = open(temp, O_RDONLY )) == -1)
return (0);
if ( read(fd, &disk, SIZEOF_DISKHEADER) < SIZEOF_DISKHEADER )
{
close(fd);
return (0);
}
(void) close(fd);
if (disk.disk_magic == APPLEDOUBLE)
return(2);
else
return(0);
}
}
}
/*
** make_apple
** This function converts the input file into an Apple Single file.
** This routine will create a new file "output".
**
** Arguments
** infile input file name
** output output file name
** creator what to put into the creator field in the finder
** type what to put into the type field in the finder
**
** Returns
** Nothing
*/
make_apple(infile,output,creator,type)
char *infile;
char *output;
char *creator;
char *type;
{
register int fd; /* Descriptor to the new file */
/*
** Create and open the output file and open it.
*/
if ((fd = creat(output,CREATE_MODE)) == -1)
{
fprintf(stderr, "make_apple: create of %s failed ",output);
perror("");
exit(1);
}
/*
** Write the header information.
*/
write_header(fd,infile);
/*
** Write the finder information and data, write_rest will close fd.
*/
write_rest(fd, infile, creator, type);
}
/*
** write_header
** This function writes the Apple Single header information
** to the output file.
**
** Arguments
** fd open file descriptor to the output file
** infile the file to convert to Apple Single format.
**
** Returns
** Nothing
*/
write_header(fd,infile)
register int fd;
char *infile;
{
diskheader disk; /* On disk header structure for Apple Single */
appleentry entry; /* On disk structure for each entry */
struct stat buf; /* Used to determine the size of the entry */
char *lastPart;
/*
** set up the information for the header and write it to the file
*/
memset(&disk, 0, sizeof(diskheader));
disk.disk_magic = APPLESINGLE;
disk.disk_version = VERSION_2;
disk.disk_numentries = 3;
if ( write(fd, &disk, SIZEOF_DISKHEADER) != SIZEOF_DISKHEADER )
{
perror("write_header: write failed");
close(fd);
exit(1);
}
/*
** set up and write the entry information for the finder
*/
entry.entry_id = ENTRY_FINDERINFO;
entry.entry_offset = FINDER;
entry.entry_length = FINDER_SIZE;
if ( write(fd, &entry, sizeof(entry)) != sizeof(entry) )
{
perror("write_header: write of finder info failed");
close(fd);
exit(1);
}
/*
** set up the realname entry
*/
entry.entry_id = ENTRY_REALNAME;
entry.entry_offset = REALNAME;
/*
** write the original file name, but only the last
** portion of a potentially full pathname.
*/
if((lastPart = strrchr(infile,'/')) == NULL)
lastPart = infile;
else
lastPart++;
entry.entry_length = strlen(lastPart);
if ( write(fd, &entry, sizeof(entry)) != sizeof(entry) )
{
perror("write_header: write of finder info failed");
close(fd);
exit(1);
}
/*
** set up and write the entry information for the data fork
*/
entry.entry_id = ENTRY_DATAFORK;
entry.entry_offset = PADLENGTH;
if( stat(infile,&buf) == -1)
{
perror("write_header: stat failed");
close(fd);
exit(1);
}
entry.entry_length = buf.st_size;
if ( write(fd, &entry, sizeof(entry)) != sizeof(entry) )
{
perror("write_header: write data fork failed");
close(fd);
exit(1);
}
}
/*
** write_rest
** This function writes the finder information and data.
** Note, this function will close fd.
**
** Arguments
** fd open file descriptor to the output file
** infile the file to convert to Apple Single format.
** creator what to put in the creator field in the finder
** type what to put in the type field in the finder
**
** Returns
** Nothing
*/
write_rest(fd, infile, creator, type)
register int fd;
char *infile;
char *creator;
char *type;
{
register int ifd; /* Used to read infile */
register int len; /* Return value from read */
char buf[BUFSIZ]; /* Used to read infile */
char *lastPart; /* the last part of the name */
/*
** First, seek to where the "real name" goes
*/
if((lseek(fd, REALNAME, 0)) == -1)
{
perror("write_rest: lseek to REALNAME offset failed");
(void) close(fd);
exit(1);
}
/*
** write the original file name, but only the last
** portion of a potentially full pathname.
*/
if((lastPart = strrchr(infile,'/')) == NULL)
lastPart = infile;
else
lastPart++;
if ( write(fd, lastPart, strlen(lastPart)) != strlen(lastPart) )
{
perror("write_rest: write of infile name failed");
(void) close(fd);
exit(1);
}
/*
** next seek to where the finder information is.
*/
if((lseek(fd, FINDER, 0)) == -1)
{
perror("write_rest: lseek to FINDER offset failed");
(void) close(fd);
exit(1);
}
/*
** write the creator and type information
*/
if ( write(fd, type, TYPE_LEN) != TYPE_LEN )
{
perror("write_rest: write of type failed");
(void) close(fd);
exit(1);
}
if ( write(fd, creator, CREATOR_LEN) != CREATOR_LEN )
{
perror("write_rest: write of creator failed");
(void) close(fd);
exit(1);
}
/*
** lseek to the PADLENGTH for the resource piece
** TOPS requires this. PADLENGTH is relative to the beginning
** of the file.
*/
if( lseek(fd, PADLENGTH, 0) == -1)
{
perror("write_rest: lseek failed");
(void) close(fd);
exit(1);
}
/*
** open the input file and write it's contents to the output
** file
*/
if( (ifd = open(infile, O_RDONLY )) == -1)
{
fprintf(stderr, "write_rest: open of %s failed ",infile);
perror("");
(void) close(fd);
exit(1);
}
/*
** Copy the contents of infile to the data fork of the out file.
*/
for (;;)
{
/*
** Read the data, <=0 hopefully means EOF.
*/
if((len = read(ifd, buf, sizeof (buf))) <= 0)
break;
/*
** Write the data to the output file.
*/
if( write(fd, buf, len) != len )
{
perror("write_rest: write of data fork failed");
(void) close(ifd);
(void) close(fd);
exit(1);
}
}
(void) close(fd);
(void) close(ifd);
}
/*
** unmake_apple
** This function splits the apple single file into the resource and data
** forks.
**
** Arguments
** infile the file to convert to split
** output the name of the output file
** got_s whether we got a -s as an option
** got_s whether we got a -o as an option
**
** Returns
** 0 on success
** -1 on error
*/
unmake_apple(infile,output,got_s,got_o)
char *infile;
char *output;
int got_s;
int got_o;
{
diskheader disk; /* On disk structure of the file */
register int fd; /* Descriptor of the file */
appleentry entry[ENTRY_MAX]; /* Each entry (resource, data, etc.) */
int i; /* Index variable */
int data_num; /* Entry for the data fork */
int unpack = 0; /* Does this file need to be unpacked? */
char creator[CREATOR_LEN]; /* Creator of the file */
char type[TYPE_LEN]; /* Type of the file */
struct stat st; /* Used to check for output file */
/*
** open the file and try reading the header information
*/
if((fd = open(infile, O_RDONLY )) == -1)
{
fprintf(stderr, "unmake_apple: open of %s failed ",infile);
perror("");
exit(1);
}
/*
** Read the disk information in.
*/
if( read(fd, &disk, SIZEOF_DISKHEADER) != SIZEOF_DISKHEADER )
{
perror ("unmake_apple: read of disk information failed");
exit(1);
}
/*
** read all of the entries
*/
for (i = 0; i < disk.disk_numentries; i++)
{
if( read(fd, &entry[i], sizeof(entry[i])) != sizeof(entry[i]) )
{
perror("unmake_apple: read of disk entry failed");
exit (1);
}
}
for (i = 0; i < disk.disk_numentries; i++)
{
/*
** save the entry number of the data fork for later use
** if the file is packed
*/
if (entry[i].entry_id == ENTRY_DATAFORK)
{
data_num = i;
}
/*
** determine if the file is in packed format by looking at the
** finderinfo
*/
if (entry[i].entry_id == ENTRY_FINDERINFO)
{
if( lseek(fd, entry[i].entry_offset, 0) == -1)
{
perror("unmake_apple: lseek failed");
exit (1);
}
if( read(fd, creator, sizeof(creator)) == -1)
{
perror("unmake_apple: read of creator failed");
exit (1);
}
if( read(fd, type, sizeof(type)) == -1)
{
perror("unmake_apple: read of type failed");
close(fd);
exit (1);
}
/*
** Check to see if it is in PACKIT format.
*/
if ( (!(strncmp(creator, "PIT ", 4))) || (!(strncmp(type, "PIT ", 4))))
{
unpack++;
}
}
}
/*
** if the file is packed then unpack it.
*/
if(unpack)
{
if ( got_o )
{
fprintf(stderr,"Can not specify output file for a packed file\n");
exit(1);
}
unpack_file(fd, entry[data_num]);
(void) close(fd);
return(0);
}
if ( !got_o )
{
output = DEF_FILE;
printf("Using output file %s\n",output);
}
if ( stat(output,&st) != -1 )
{
fprintf(stderr,"Output file %s already exists\n",output);
exit(1);
}
/*
** create the appropriate files
*/
make_header(disk, entry, output, got_s);
make_res_data(fd, disk.disk_numentries, entry, output, got_s);
if( !got_s )
{
pad_resource(output);
}
(void) close(fd);
return(0);
}
/*
** make_header
** This function writes the header information for a file.
**
** Arguments
** disk the disk header information in the input file
** entry array of the entry fields of the input file
** output the name of the output file
** got_s whether we got a -s as an option
**
** Returns
** Nothing
*/
make_header(disk, entry, output, got_s)
diskheader disk;
appleentry entry[ENTRY_MAX];
char *output;
int got_s;
{
register int fd; /* File descriptor to new file */
char tmp[MAXPATHLEN];/* Name of the resource fork */
short temp; /* Temp holder */
int i; /* index variable */
int have_data = 0; /* Number of data forks */
appleentry nulls; /* Empty entry for padding */
/*
** if we got a -s then create a file with %h followed by the name
** of the output file. Otherwise, create a file with % followed by the
** name of the ouput file
*/
if (got_s)
{
doubleName(output,tmp,1);
disk.disk_magic = APPLEUNKNOWN;
}
else
{
doubleName(output,tmp,0);
disk.disk_magic = APPLEDOUBLE;
}
if ((fd = creat(tmp,CREATE_MODE)) == -1)
{
fprintf(stderr, "make_header: create of %s failed: ",tmp);
perror("");
exit(1);
}
/*
** since we are spilting the file don't write the entry for the data
** but we need to decrement the number of entries
*/
temp = disk.disk_numentries;
for(i = 0; i < disk.disk_numentries; i++)
{
if(entry[i].entry_id == ENTRY_DATAFORK)
{
have_data++;
temp--;
}
}
disk.disk_numentries = temp;
/*
** write the disk information to the header
*/
if ( write(fd, &disk, SIZEOF_DISKHEADER) != SIZEOF_DISKHEADER )
{
perror("make_header: write of disk structure failed");
exit(1);
}
/*
** write all entries to the header except the data fork
*/
for(i = 0; i < disk.disk_numentries; i++)
{
if(entry[i].entry_id != ENTRY_DATAFORK)
{
if( write(fd, &entry[i], sizeof(entry[i])) != sizeof (entry[i]) )
{
perror("make_header: write of entries failed");
exit(1);
}
}
}
(void) memset((char *)&nulls, sizeof (nulls), '\0');
for ( i = disk.disk_numentries ; i < ENTRY_MAX ; i++ )
{
if ( write(fd,&nulls, sizeof(nulls)) != sizeof(nulls) )
{
perror("make_header: write of null entry failed");
exit(1);
}
}
/*
** restore the number of entries
*/
if(have_data)
disk.disk_numentries += have_data;
(void) close(fd);
}
/*
** make_res_data
** This function writes the resource and data forks
**
** Arguments
** fd open file descriptor to the input file
** numentries the number of entries in the input file
** entry array of the entry fields of the input file
** output the name of the output file
** got_s whether we got a -s as an option
**
** Returns
** 0 on success
** -1 on error
*/
make_res_data(fd, numentries, entry, output, got_s)
register int fd;
short numentries;
appleentry entry[ENTRY_MAX];
char *output;
int got_s;
{
register int rfd; /* Resource fork descriptor */
register int dfd; /* Data fork descriptor */
char *file; /* Pointer to the file name */
char temp[MAXPATHLEN]; /* Storage for the file name */
char buf[BUFSIZ]; /* Buffer for reading the data */
int i; /* Index variable */
int no_data = 1; /* Tells us if anything was ever
** written to the data fork.
*/
long num = 0; /* Length of each entry */
int len = 0; /* Length returned by read */
doubleName(output,temp,0);
file = temp;
/*
** if we got a -s then create and open the %output file
** otherwise, just open it
*/
if(got_s)
{
if ((rfd = creat(file,CREATE_MODE)) == -1)
{
fprintf(stderr, "make_res_data: creat of %s failed: ",file);
perror("");
exit(1);
}
}
else
{
if((rfd = open(file, O_RDWR )) == -1)
{
fprintf(stderr, "make_res_data: open of %s failed: ",file);
perror("");
exit(1);
}
if ( lseek(rfd,0L,2) == -1 )
{
fprintf(stderr,"make_res_data: lseek failed: ");
perror("");
exit(1);
}
}
/*
** create the output file for the data fork
*/
if ((dfd = creat(output,CREATE_MODE)) == -1)
{
fprintf(stderr, "make_res_data: creat of %s failed: ",output);
perror("");
exit(1);
}
/*
** Write all of the data to the %output file unless
** it is the data fork in which case write it to output file.
*/
for (i = 0; i < numentries; i++)
{
/*
** read the data
*/
if( lseek(fd, entry[i].entry_offset, 0) != entry[i].entry_offset )
{
perror("make_res_data: lseek failed");
exit(1);
}
/* !!!!always!!! seek to the position to write in the resource. */
if( entry[i].entry_id != ENTRY_DATAFORK)
{
if( lseek(rfd, entry[i].entry_offset, 0) != entry[i].entry_offset )
{
perror("make_res_data: lseek failed");
exit(1);
}
}
num = entry[i].entry_length;
while(num)
{
if((len = read(fd, buf, min(num, sizeof(buf)))) <= 0)
{
break;
}
/*
** write the data
*/
if( entry[i].entry_id == ENTRY_DATAFORK)
{
/*
** if we have the data fork then write to the output
*/
no_data = 0;
if( write(dfd, buf, len) != len )
{
perror("make_res_data: write to data fork failed");
exit(1);
}
}
else
{
/*
** otherwise write to the %output file
*/
if( write(rfd, buf, len) != len )
{
perror("make_res_data: write of fork failed");
exit(1);
}
}
num -= len;
}
}
/*
** if there is no data fork then unlink the output file
*/
if (no_data)
(void) unlink(output);
(void) close(fd);
(void) close(dfd);
(void) close(rfd);
}
/*
** pad_resource
** This function will pad the resource fork to PADLENGTH (256)
** if it is smaller than that. This is used only for APPLEDOUBLE files.
**
** Arguments
** output output file name
**
** Returns
** Nothing
*/
pad_resource(output)
char *output;
{
int fd; /* descriptor to the file */
char temp[MAXPATHLEN]; /* Resource fork name of the file */
struct stat st; /* Stat structure for determining the pad length */
char eof; /* Temp for holding EOF */
/*
** append a % to the file name
*/
doubleName(output,temp,0);
/*
** open this file and pad it if it is smaller than 256 bytes
*/
if((fd = open(temp, O_RDWR )) == -1)
{
fprintf(stderr, "pad_resource: open of %s failed: ",temp);
perror("");
exit(1);
}
if ( stat(temp,&st ) == -1)
{
perror("pad_resource: stat of output file failed");
exit(1);
}
if (st.st_size < PADLENGTH)
{
if ( lseek(fd, PADLENGTH-1, 0) == -1 )
{
perror("pad_resource: lseek(PADLENGTH - 1) failed");
exit(1);
}
eof = EOF;
if ( write(fd,&eof,1) != 1 )
{
perror("pad_resource: write(EOF) failed");
exit(1);
}
}
(void) close(fd);
}
/*
** This function converts an apple double file into an apple single file
**
** Arguments
** infile the name of the input file
** output output file name
** whther the input file starts with a %
**
** Returns
** 0 on success
** -1 on failure
*/
double_to_single(infile, output, got_per)
char *infile;
char *output;
int got_per;
{
diskheader disk; /* On disk layout of file */
appleentry entry[ENTRY_MAX]; /* Structure of each entry */
int fd; /* File descriptor */
int rfd; /* Descriptor for resource file */
int ofd; /* Descriptor for data file */
int i; /* Index variable */
long num; /* Number of bytes to write out */
int len; /* Length returned by read */
struct stat st; /* Used to determine number of bytes to write out */
char file[MAXPATHLEN]; /* Name of the output file */
char *file1; /* Pointer to input file */
char temp1[MAXPATHLEN]; /* Another output file */
char buf[BUFSIZ]; /* Buffer for reading file */
file1 = infile;
if(got_per)
{
/*
** we got the file starting with a percent.
** try opening the file without the percent.
** if we can't because the file does not exists then
** just create the output file and dump the input file into it.
** while changing the magic number from appledouble to
** applesingle
*/
singleName(infile,file);
if ((fd = open(file, O_RDONLY )) == -1)
{
if(errno == ENOENT)
{
/*
** create and open the output file
*/
if ((ofd = creat(output,CREATE_MODE)) == -1)
{
fprintf(stderr, "double_to_single: creat of %s failed: ",output);
perror("");
exit(1);
}
/*
** open the input file
*/
if((fd = open(infile, O_RDONLY )) == -1)
{
fprintf(stderr, "double_to_single: open on %s failed: ",infile);
perror("");
exit (1);
}
/*
** stat the input file
** so that we can figure out its size
*/
if( stat(infile, &st) == -1)
{
perror ("double_to_single: stat failed");
exit (1);
}
if( read(fd, &disk, SIZEOF_DISKHEADER) != SIZEOF_DISKHEADER )
{
perror ("double_to_single: read of header failed");
exit (1);
}
/*
** change the magic number to applesingle
*/
disk.disk_magic = APPLESINGLE;
/*
** write the header
*/
if( write(ofd, &disk, SIZEOF_DISKHEADER) != SIZEOF_DISKHEADER )
{
perror ("double_to_single: write of header failed");
exit (1);
}
/*
** lseek past the header and read the rest
** into a buffer
*/
if( lseek(fd, (long)SIZEOF_DISKHEADER, 0) == -1)
{
perror ("double_to_single: seek past header failed");
exit (1);
}
if( lseek(ofd, (long)SIZEOF_DISKHEADER, 0) == -1)
{
perror ("double_to_single: seek on output file failed");
exit (1);
}
num = st.st_size-SIZEOF_DISKHEADER;
while(num)
{
if((len = read(fd, buf, min(num, sizeof(buf)))) <= 0)
{
break;
}
/*
** write the buffer
*/
if((write(ofd, buf, len)) != len )
{
perror ("double_to_single: write of buffer failed");
exit (1);
}
num -= len;
}
(void) close(fd);
(void) close(ofd);
return(0);
}
else
{
fprintf(stderr, "double_to_single: open on %s failed: ",file);
perror("");
exit(1);
}
}
}
else
{
/*
** open the input file since it does not start with a percent
*/
strcpy(file, infile);
doubleName(infile,temp1,0);
file1 = temp1;
if ((fd = open(file, O_RDONLY )) == -1)
{
fprintf(stderr, "double_to_single: open on %s failed: ",file);
perror("");
exit(1);
}
}
/*
** open the file starting with the percent
** if it does not exists then exit since the
** file can't be apple double
*/
if ((rfd = open(file1, O_RDONLY )) == -1)
{
fprintf(stderr, "double_to_single: open on %s failed: ",file1);
perror("");
exit(1);
}
/*
** create and open the output file
*/
if ((ofd = creat(output,CREATE_MODE)) == -1)
{
fprintf(stderr, "double_to_single: create of %s failed: ",output);
perror("");
exit(1);
}
/*
** read the disk and header info from the resource file
*/
if( read(rfd, &disk, SIZEOF_DISKHEADER) != SIZEOF_DISKHEADER )
{
perror ("double_to_single: read of resource header failed");
exit (1);
}
for (i = 0; i < disk.disk_numentries; i++)
{
if( read(rfd, &entry[i], sizeof(entry[i]))
!= sizeof(entry[i]) )
{
perror("double_to_single: read of disk entries failed");
exit (1);
}
}
/*
** stat the data fork to get its size
*/
if((stat(file,&st)) == -1)
{
perror("double_to_single: stat failed");
exit(1);
}
/*
** set-up the header for the output file and entry info
** to include the data fork
*/
disk.disk_magic = APPLESINGLE;
entry[disk.disk_numentries].entry_id = ENTRY_DATAFORK;
/*
** the length is the length of the data file
*/
entry[disk.disk_numentries].entry_length = (long)st.st_size;
if( stat(file1,&st) == -1)
{
perror("double_to_single: stat failed");
exit(1);
}
/*
** the offset is after size of the resource fork
*/
entry[disk.disk_numentries].entry_offset = st.st_size;
/*
** write the header and entry info
*/
disk.disk_numentries++;
if ( write(ofd, &disk, SIZEOF_DISKHEADER) != SIZEOF_DISKHEADER )
{
perror("double_to_single: write of disk header failed");
exit(1);
}
for (i = 0; i < disk.disk_numentries; i++)
{
if ( write(ofd, &entry[i], sizeof(entry[i])) != sizeof(entry[i]) )
{
perror("double_to_single: write of disk entries failed");
exit(1);
}
}
/*
** write the data and resource forks into the output file
*/
for (i = 0; i < disk.disk_numentries; i++)
{
if( lseek(rfd, entry[i].entry_offset, 0) == -1)
{
perror("double_to_single: lseek to entry failed");
exit(1);
}
if( lseek(ofd, entry[i].entry_offset, 0) == -1)
{
perror("double_to_single: lseek on output file to entry failed");
exit(1);
}
/*
** read the data
** if the entry is data fork then read from the data fork file
** otherwise read from the resource fork file
*/
num = entry[i].entry_length;
while(num)
{
if((entry[i].entry_id) == ENTRY_DATAFORK)
{
if((len = read(fd, buf, min(num, sizeof(buf)))) <= 0)
{
break;
}
}
else
{
if ((len = read(rfd, buf, min(num,sizeof(buf)))) <= 0)
{
break;
}
}
/*
** write the buffer
*/
if( write(ofd, buf, len) != len )
{
perror("double_to_single: write of data failed");
exit(1);
}
num -= len;
}
}
(void) close(fd);
(void) close(rfd);
(void) close(ofd);
return(0);
}
/*
** unpack_file
** This function unpacks a packed apple file
**
** Arguments
** fd the file descriptor to the input file
** entry the data fork entry information
**
** Returns
** 0 on success
** -1 on failure
*/
unpack_file(fd, entry)
register int fd;
appleentry entry;
{
diskheader disk; /* Structure for each file in the packed file */
appleentry newentry[ENTRY_MAX]; /* For the entries in each packed file */
ENCL encl; /* PACKIT structure for each enclosed file */
ENCLCRC hcrc; /* CRC for ENCL header */
ENCLHEADER header; /* Type of each packed file (PACK_COMP,
PACK_UNCOMP, PACK_END) */
char buf[BUFSIZ]; /* Used to read the file */
char filename[MAXNAMLEN]; /* Output file name */
int tfd; /* Output file descriptor */
long num; /* Number of bytes to read/write */
int len = 0; /* Value returned by read */
int i; /* Index variable */
memset(&disk, 0, sizeof(diskheader));
/*
** seek to the data fork
*/
if ( lseek (fd, entry.entry_offset, 0) == -1)
{
perror("unpack_file: initial lseek failed");
exit(1);
}
/*
** the packed files are seperated by PACK_END
** read the files until we have a PACK_END string
*/
while ( read(fd, header, sizeof(header)) != -1
&& (strncmp(header,PACK_END,sizeof (header))))
{
/*
** we don't support these kinds of packed files
*/
if ( strncmp(header,PACK_UNCOMP,sizeof (header))
&& strncmp(header,PACK_COMP,sizeof (header)) )
{
fprintf(stderr,"Can only understand PMag, and PMa4 packed files\n");
exit(1);
}
/*
** read the enclosure information
*/
if( read(fd, &encl, sizeof(encl)) != sizeof(encl) )
{
perror("unpack_file: read of enclosure info failed");
exit(1);
}
if( read(fd, &hcrc, sizeof(hcrc)) != sizeof(hcrc) )
{
perror("unpack_file: read of crc failed");
exit(1);
}
/*
** encl.fileName is the realname of the packed file.
** create and open it.
*/
strncpy(filename, encl.fileName, encl.fileNameSize);
filename[encl.fileNameSize] = '\0';
if ((tfd = creat(filename,CREATE_MODE)) == -1)
{
fprintf(stderr, "unpack_file: create of %s failed: ",filename);
perror("");
exit(1);
}
/*
** set-up and write the disk header info
*/
disk.disk_magic = APPLESINGLE;
disk.disk_version = VERSION_2;
disk.disk_numentries = 4;
if ( write(tfd, &disk, SIZEOF_DISKHEADER) != SIZEOF_DISKHEADER )
{
perror("unpack_file: write of disk header failed");
exit(1);
}
/*
** set up the realname entry
*/
newentry[0].entry_id = ENTRY_REALNAME;
newentry[0].entry_offset = REALNAME;
newentry[0].entry_length = encl.fileNameSize;
/*
** set up the finderinfo entry
*/
newentry[1].entry_id = ENTRY_FINDERINFO;
newentry[1].entry_offset = FINDER;
newentry[1].entry_length = FINDER_SIZE;
/*
** set up the resourcefork entry
*/
newentry[2].entry_id = ENTRY_RESOURCEFORK;
newentry[2].entry_offset = PADLENGTH;
newentry[2].entry_length = encl.resourceSize;
/*
** set up the datafork entry
*/
newentry[3].entry_id = ENTRY_DATAFORK;
newentry[3].entry_offset = PADLENGTH + encl.resourceSize;
newentry[3].entry_length = encl.dataSize;
/*
** write all entries to the output
*/
for (i=0; i < disk.disk_numentries; i++)
{
if( write(tfd, &newentry[i], sizeof(newentry[i])) != sizeof(newentry[i]) )
{
perror("unpack_file: write of entry fields failed");
exit(1);
}
}
/*
** write the realname entry data
*/
if( lseek(tfd, newentry[0].entry_offset, 0) == -1)
{
perror("unpack_file: lseek for realname failed");
exit(1);
}
if( write(tfd,encl.fileName, encl.fileNameSize) != encl.fileNameSize )
{
perror("unpack_file: write for realname failed");
exit(1);
}
/*
** write the finderinfo entry data
*/
if( lseek(tfd, newentry[1].entry_offset, 0) == -1)
{
perror("unpack_file: lseek for finder info failed");
exit(1);
}
if( write(tfd, encl.fileType, sizeof(encl.fileType))
!= sizeof(encl.fileType) )
{
perror("unpack_file: write for type info failed");
exit(1);
}
if( write(tfd,encl.fileCreator, sizeof(encl.fileCreator))
!= sizeof(encl.fileCreator) )
{
perror("unpack_file: write for creator info failed");
exit(1);
}
/*
** write the resourcefork entry data
*/
if( lseek(tfd, newentry[3].entry_offset, 0) == -1)
{
perror("unpack_file: lseek resource fork failed");
exit(1);
}
num = encl.dataSize;
while(num)
{
if((len = read(fd, buf, min(num,sizeof(buf)))) <= 0)
{
break;
}
if( write(tfd, buf, len) != len )
{
perror("unpack_file: write for resource fork failed");
exit(1);
}
num -= len;
}
if( lseek(tfd, newentry[2].entry_offset, 0) == -1)
{
perror("unpack_file: lseek for data fork failed");
exit(1);
}
/*
** write the datafork entry data
*/
num = encl.resourceSize;
while(num)
{
if( (len = read(fd, buf,len = min(num, sizeof(buf)))) <= 0)
{
break;
}
if( write(tfd, buf, len) != len )
{
perror("unpack_file: write for data fork failed");
exit(1);
}
num -= len;
}
/*
** read past the last 2 bytes
*/
if( read(fd, &hcrc, sizeof(hcrc)) != sizeof(hcrc) )
{
perror("unpack_file: read of final CRC failed");
exit(1);
}
(void) close(tfd);
}
}
/*
** doubleName
** Convert a file name into a resource fork name. This means adding
** either a '%', or a '%h' to the front of the last component of the
** file name (i.e. /a/b/c/file -> /a/b/c/%file).
**
** Arguments
** input The initial file name
** output The place to put the new file name (assumed to have enough space)
** hname Boolean, should the prefix be "%", or "%h"?
**
** Returns
** Nothing
*/
doubleName(input,output,hname)
char *input;
char *output;
int hname;
{
char *slash; /* The last '/' in the input file */
char lastname[MAXNAMLEN];
char *prefix; /* Prefix to change the lastname with */
int len; /* Length of input name */
if ( hname )
prefix = "%h";
else
prefix = "%";
if ( (len = strlen(input)) > 1 )
{
if ( input[len - 1] == '/' )
input[len - 1] = 0;
}
if ( (slash = strrchr(input,'/')) == NULL )
sprintf(output,"%s%s",prefix,input);
else
{
/* A copy of the last component of the file name. */
strcpy(lastname,slash + 1);
strcpy(output,input);
slash = &output[slash - input];
strcpy(slash + 1,prefix);
if ( hname )
strcpy(slash + 3,lastname);
else
strcpy(slash + 2,lastname);
}
}
/*
** singleName
** Convert a file name from a resource fork name, into the data fork name.
** The '%' prefix is striped off of the file name
** (i.e. /a/b/c/%file -> /a/b/c/file).
**
** Arguments
** input The initial file name
** output The place to put the new file name (assumed to have enough space)
** Returns
** Nothing
*/
singleName(input,output)
char *input;
char *output;
{
char *percent; /* Location of the last '%' in the file name. */
char lastname[MAXNAMLEN];
if ( (percent = strrchr(input,'%')) == NULL )
strcpy(output,input);
else
{
/* A copy of the last component of the file name. */
strcpy(lastname,percent + 1);
*percent = 0;
strcpy(output,input);
strcat(output,lastname);
*percent = '%';
}
}